#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include "msg.h"
#include "error.h"

// 常量
#define SERV_PORT 8001
#define FRIEND_MAX  100
#define FILE_SIZE 1024

// 函数声明
int connect_server();                            // 连接服务器
void* read_msg (void* v);                        // 使用线程读写分离
void welcome_menu();                             // 打印欢迎界面
void exit_client(int client_socket, Msg* msg);   // 退出客户端
void login(int client_socket, Msg* msg, int* welcome); // 登录
int regis(int client_socket,Msg msg);            // 注册
void chat_menu();                                // 打印聊天页面
void chat_all(int client_socket,Msg msg);        // 群聊
void chat_one(int client_socket,Msg msg);        // 发送好友申请
void contact_list(int client_socket, Msg msg);   // 查看好友列表
void group_records(int client_socket,Msg msg);   // 发起查看群聊记录
void private_records(int client_socket,Msg msg); // 发起查看私聊记录
void send_file(int client_socket,Msg msg);       // 发送文件
void logout_prompt(Msg* msg);                    // 下线提醒
void group_msg(Msg* msg);                        // 接收群聊消息
void friend_request(int client_socket, Msg* msg);// 接收好友申请，并发送反馈
void private_msg(int client_socket,Msg* msg);    // 接收私聊消息
void chat_feedback(Msg* msg);                    // 是否同意好友申请
void get_private_records(Msg* msg);              // 接收服务端响应的私聊聊记录
void get_group_records(Msg* msg);                 // 接收服务端响应的群聊记录
int generateID();                                // 生成不重复的ID作为文件名
char* get_time();                                // 获取时间



int main(){
    struct Msg msg;
    int create_pthread = 1;
    int welcome = 1;
    int chat_index = 1; 
    int send_len,recv_len;

    memset(&msg, 0, sizeof(msg));
    int client_socket = connect_server(); // 连接服务器

    while (welcome){
        // 打印欢迎界面
        welcome_menu();
        scanf("%d",&msg.header.cmd);
        switch (msg.header.cmd){   
            case 0:     // 退出客户端
                exit_client(client_socket, &msg);   
                // 退出循环
                welcome = 0;
                create_pthread = 0;
                break;
            case 1:     // 登录
                login(client_socket, &msg, &welcome);   
                break;
            case 2:     // 注册
                regis(client_socket,msg);       
                break;
            default:
                printf("没有这个命令,请重新输入\n");
                break;
        }
    }

    while (create_pthread){
        // 阻塞，等待客户端连接，成功则返回新的文件描述符，用于和客户端通信
        // 在64位系统中，void*的大小是8字节，而int的大小是4字节,因此这里使用动态内存分配
        int *pclient = malloc(sizeof(int));
        *pclient = client_socket;
        pthread_t read_id;
        pthread_create(&read_id, NULL, read_msg, pclient);
        pthread_detach(read_id); 
        
        
        while(chat_index){
            // 打印聊天页面
            chat_menu();
            printf("请输入您要做操作\n");
		    scanf("%d",&msg.header.cmd);
            switch(msg.header.cmd){
                case 3:     //发送群聊消息
                    chat_all(client_socket,msg);
                    break;
                case 4:     //发送好友申请
                    chat_one(client_socket,msg);
                    break;
                case 5:     //好友列表
                    contact_list(client_socket,msg);
                    break;
                case 6:     //查看群聊记录
                    group_records(client_socket,msg);
                    break;
                case 7:     //查看私聊记录
                    private_records(client_socket,msg);
                    break;
                case 0:     //退出
                    send_len = send(client_socket, &msg, sizeof(Msg), 0);
                    error_message(send_len,"数据发送失败\n");
                    // 设置循环变量为0即可退出循环
                    chat_index = 0;
                    create_pthread = 0;
                    break;
                default:
                    printf("没有该命令,请重新输入\n");
                    break;
            }
        }
        
    }
    
    close(client_socket);
    return 0;
}



//读写分离
void * read_msg (void *arg){
	int client_socket = *(int*)arg;
	Msg msg;
	int read_pthread = 1;
    int recv_len,send_len;
    memset(&msg, 0, sizeof(msg));

	while(read_pthread){
		int recv_len = recv(client_socket, &msg, sizeof(Msg),0);
        error_message(recv_len,"数据接收失败\n");
		switch(msg.header.cmd){
            case 11:     // 接收下线提醒
                logout_prompt(&msg);
				break;
			case 12:    //接收群聊消息
				group_msg(&msg);
				break;
            case 13:    //接收好友申请，并发送反馈
                friend_request(client_socket, &msg);
				break;
            case 14:    // 接收私聊消息
                private_msg(client_socket,&msg);
                break;
            case 16:   // 是否同意好友申请
                chat_feedback(&msg);
                break;
            case 17:   // 获取服务端响应的群聊记录
                get_group_records(&msg);
                break;
            case 18:   // 获取服务端响应的私聊记录
                get_private_records(&msg);
                break;
			default:
                printf("服务器响应出错,请联系管理员\n");
                break;
		}
	}
}



//连接服务器
int connect_server(){
    int client_socket;
    struct sockaddr_in addr;

	//创建套接字
	client_socket = socket(AF_INET,SOCK_STREAM,0);
    error_message(client_socket,"创建套接字失败\n");
    bzero(&addr,sizeof(addr));

	//链接服务器
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(SERV_PORT);
	inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr.s_addr);
	
	int con = connect(client_socket,(struct sockaddr *)&addr, sizeof(addr));
    error_message(con,"链接服务器失败\n");

	printf("成功连接到服务器 : %s\n",inet_ntoa(addr.sin_addr));
	
	return client_socket;
}


// 登录
void login(int client_socket, Msg* msg, int* welcome) {
    int send_len, recv_len;
    printf("请输入用户名: ");
    scanf("%s",msg->login.username);
    printf("请输入密码: ");
    scanf("%s",msg->login.password);
    // 对输入的用户名和密码做长度限制
    if(strlen(msg->login.username) > 10 || strlen(msg->login.password) > 10){
        printf("用户名与密码长度不能大于10，请重新输入\n");
        return;
    }
    send_len = send(client_socket, msg, sizeof(Msg), 0);
    error_message(send_len,"数据发送失败\n");
    recv_len = recv(client_socket, msg, sizeof(Msg), 0);
    error_message(recv_len,"数据接收失败\n");

    if (msg->login.isLogin == 1) {
        printf("登陆成功！\n");
        *welcome = 0;
    } else if (msg->login.isLogin == 2){
        printf("登陆失败,用户名或密码错误!\n");
    } else if (msg->login.isLogin == 3){
        printf("登陆失败！该用户已登录,无法重复登录!\n");
    } else{
        printf("未知错误,请联系管理员!\n");
    }
}


// 注册
int regis(int client_socket,Msg msg){
    int send_len,recv_len;
    printf("请输入用户名: \n");
    scanf("%s", msg.regisc.username);
    printf("请输入密码: \n");
    scanf("%s", msg.regisc.password);
    if (strlen(msg.regisc.username) > 10 || strlen(msg.regisc.password) > 10){
        printf("用户名与密码长度不能大于10，请重新输入\n");
        return 0;
    }
    printf("正在注册，请稍后......\n");
    sleep(1);
    send_len = send(client_socket, &msg, sizeof(msg), 0);
    error_message(send_len,"数据发送失败\n");
    recv_len = recv(client_socket, &msg, sizeof(msg), 0); 
    error_message(recv_len,"数据接收失败\n");

    if (msg.regisc.status == 1){
        printf("注册成功！\n");
        return 1;
    } else if (msg.regisc.status == 0) {
        printf("注册失败！该用户名已经存在\n");
        return 0;
    } else {
        printf("未知错误！\n");
        return 0;
    }
}



//群聊
void chat_all(int client_socket,Msg msg){
    // 将发送人设置为当前登录用户
    strcpy(msg.chat.fromUser,msg.login.username);
    // 设置消息时间
    strcpy(msg.chat.time,get_time());
    printf("请输入你要群发送的信息\n");
    scanf("%s",msg.chat.message);
    printf("发送人:%s\n",msg.login.username);
    msg.chat.group_or_private = 1;
    int send_len = send(client_socket, &msg, sizeof(Msg),0);
    error_message(send_len,"数据发送失败\n");
}


//发送好友申请
void chat_one(int client_socket,Msg msg){
    printf("请输入你要聊天的用户名\n");
    scanf ("%s",msg.chat.toUser);
    int send_len = send(client_socket, &msg, sizeof(Msg),0);
    error_message(send_len,"数据发送失败\n");
}

// 查看好友列表
void contact_list(int client_socket,Msg msg){
    int recv_len,send_len;
    FriendList variable_friends[FRIEND_MAX];    // 用于临时存储当前用户好友列表
    bzero(variable_friends,sizeof(variable_friends));
    // 向服务器获取当前用户的好友列表
    msg.header.cmd = 6;
    send_len = send(client_socket, &msg, sizeof(Msg),0);
    error_message(send_len,"数据发送失败\n");
    recv_len = recv(client_socket, &variable_friends, sizeof(variable_friends),0);
    error_message(recv_len,"数据接收失败\n");
    // 打印好友列表
    printf("-----    好友列表    -----\n");
    for (int i = 0; i < FRIEND_MAX; i++) {
    if (variable_friends[i].flag != 0 && strcmp(variable_friends[i].toUser, msg.login.username) != 0){
            printf("用户名:%s\n", variable_friends[i].toUser);
        }
    }
    printf("----------------------------\n");


    printf("请输入你要联系的用户名称\n");
    scanf("%s",msg.chat.toUser);

    
    // 遍历好友数组,从数组中查找是否有该好友
    int flag = 0;
    for (int i = 0; i < FRIEND_MAX; i++){
        // 如果输入的好友名在列表中,则正常发送消息
        if (strcmp(variable_friends[i].toUser,msg.chat.toUser) == 0){   
            // 如果好友列表中存在输入的用户名,则将flag置为1,退出循环
            flag = 1;
            break;
        }
    }

    // 如果存在该好友则开始发送消息
    if (flag){
        while (1){   // 选择退出私聊时将使用break结束循环
            // 设置发送消息命令为7
            msg.header.cmd = 7;
            // 选择发送消息的类型,可以发送文字消息或传输文件
            printf("请输入你要发送的消息类型:1.文字  2.文件  0.返回群聊\n");
            scanf("%d",&msg.chat.type);

            if (msg.chat.type==1){   // 选择发送文字消息
                printf("请输入你要发送的消息,输入all可返回群聊\n");
                scanf("%s",msg.chat.message);
                // 设置消息时间
                strcpy(msg.chat.time,get_time());
                // 如果用户输入all则退出私聊,返回到用户主页
                if (strcmp(msg.chat.message, "all")==0){
                    break;
                }
                // 设置为私聊消息
                msg.chat.group_or_private = 2;
                send(client_socket, &msg, sizeof(Msg),0);

            } else if (msg.chat.type==2){ // 选择发送文件
                // 设置发送文件的命令
                send_file(client_socket,msg);
                break;

            } else if (msg.chat.type==0){ // 选择退出私聊
                break;

            } else{     // 命令错误
                printf("消息类型选择错误,请重新选择\n");
                break;;
            }
        }
    } else{
        printf("该用户不在你的好友列表中,请重新输入\n");
    }
    
    
}


// 查看群聊记录
void group_records(int client_socket,Msg msg){
    msg.header.cmd = 10;
    send(client_socket, &msg, sizeof(msg), 0);
    recv(client_socket,&msg,sizeof(msg),0);
    printf("%s",msg.chat.message);
}


// 查看私聊记录
void private_records(int client_socket,Msg msg){
    msg.header.cmd = 11;
    send(client_socket, &msg, sizeof(Msg), 0);
}



// 发送文件
void send_file(int client_socket,Msg msg){   

    char file_path[FILE_SIZE];
    size_t bytes_read;
    int source_fd,send_len;
    char buffer[FILE_SIZE];
    msg.header.cmd = 8;
    char test[FILE_SIZE];

    printf("请输入文件路径: \n");
    scanf("%s",file_path);  
    file_path[strcspn(file_path, "\n")] = 0;
    // 打开源文件
    source_fd = open(file_path, O_RDONLY);
    if (source_fd == -1) {
        perror("文件打开失败");
        return ;
    }
    
    
    // 从源文件读取内容并写入目标文件
    bytes_read = read(source_fd, buffer, FILE_SIZE);
    strcpy(msg.chat.message, buffer);
    msg.header.cmd = 8;
    send_len = send(client_socket,&msg,sizeof(msg),0);
    close(source_fd);

}



// 下线提醒
void logout_prompt(Msg* msg) {
    printf("%s\n", msg->chat.message);
}



// 接收群聊消息
void group_msg(Msg* msg) {
    printf("%s: %s\t%s\n", msg->chat.fromUser, msg->chat.message, msg->chat.time);
}



//接收好友申请，并发送反馈
void friend_request(int client_socket, Msg* msg) {
    int send_len;
    if (msg->chat.is_on_line){
        printf("%s 请求与你私聊，是否接受？y/n?：\n", msg->chat.fromUser);
        scanf("%s", msg->chat.message);
        if(strcmp(msg->chat.message, "y") == 0){
            msg->chat.is_consent_chat = 1;   // 如果同意私聊则将值设为1
        } else{
            msg->chat.is_consent_chat = 0;   // 否则设为0
        }
        // 设置返反馈命令为42
        msg->header.cmd = 9;
        send_len = send(client_socket, msg, sizeof(Msg), 0);
        error_message(send_len,"数据发送失败\n");
    } else{
        printf("对方不在线\n");
    }
}



// 接收私聊消息
void private_msg(int client_socket,Msg* msg) {
    int val_read,send_len;
    FILE *file;
    char file_path[1024];
    char new_file_name[20];
    int write_fd;
    ssize_t bytes_written;
    
    char* file_suffix = strrchr(msg->chat.file_path, '.');  // 接收文件后缀
    int ID = generateID();               // 生成新的文件名
    sprintf(new_file_name, "%d", ID);   // int转为字符数组
    getcwd(file_path, sizeof(file_path));   // 获取当前路径
    strcat(file_path, "/client_file/"); // 拼接新文件存放的路径
    strcat(file_path, new_file_name);   // 拼接新文件名
    strcat(file_path, ".txt");     // 拼接后缀
    if (msg->chat.is_on_line) {
        if (msg->chat.type == 1){
            printf("%s: %s\t%s\n", msg->chat.fromUser, msg->chat.message, msg->chat.time);
        }else {
            // 创建或打开目标文件
            write_fd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
            if (write_fd == -1) {
                perror("Failed to open destination file");
                return ;
            }
            
            int recv_len = recv(client_socket,&msg,sizeof(msg),0);
            if (recv_len){
                bytes_written = write(write_fd, msg->chat.message, recv_len);
            }
            close(write_fd);
            printf("%s: %s\t%s\n", msg->chat.fromUser, "给你发送了一个文件", msg->chat.time);
        }
    } else{
        printf("对方不在线,按0退出私聊\n");
    }
}



// 是否同意好友申请
void chat_feedback(Msg* msg) {
    if (msg->chat.is_consent_chat) {
        printf("%s 已同意与你的好友申请\n", msg->chat.toUser);
    } else {
        printf("对方拒绝你的好友申请\n");
    }
}


// 获取群记录
void get_group_records(Msg* msg) {
    printf("%s",msg->chat.message);
}



// 获取私聊记录
void get_private_records(Msg* msg) {
    printf("%s",msg->chat.message);
}



// 退出客户端
void exit_client(int client_socket, Msg* msg) {
    int send_len;
    msg->header.cmd = 0;
    send_len = send(client_socket, msg, sizeof(Msg), 0);
    error_message(send_len,"数据发送失败\n");
}


// 生成随机不重复的文件名
int generateID() {
    time_t t;
    srand((unsigned) time(&t));
    int id = rand();
    return id;
}


// 获取时间
char* get_time() {
    setenv("TZ", "Asia/Shanghai", 1);
    tzset();
    time_t rawtime;
    struct tm * timeinfo;
    static char buffer[80];

    time (&rawtime);
    timeinfo = localtime(&rawtime);

    strftime(buffer, sizeof(buffer),"%Y-%m-%d %H:%M:%S", timeinfo);
    return buffer;
}


// 打印欢迎页面
void welcome_menu(){
    printf("\n\n\n\n");
    printf("************************************\n");
    printf("***       欢迎使用聊天室         ***\n");
    printf("***           1,登录             ***\n");
    printf("***           2,注册             ***\n");
    printf("***           0,退出             ***\n");
    printf("************************************\n");
}


// 打印用户功能页面
void chat_menu(){
    printf("\n\n\n\n");
    printf("*****************************************\n");
    printf("***    群聊页面，可直接接收群聊消息   ***\n");
    printf("***           3,群聊                  ***\n");
    printf("***           4,发送好友申请          ***\n");
    printf("***           5,好友列表              ***\n");
    printf("***           6,查看群聊记录           ***\n");
    printf("***           7,查看私聊记录           ***\n");
    printf("***           0,退出                  ***\n");
    printf("*****************************************\n");
}